/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// TGUI - Texus' Graphical User Interface
// Copyright (C) 2012-2025 Bruno Van de Velde (vdv_b@tgui.eu)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
//    you must not claim that you wrote the original software.
//    If you use this software in a product, an acknowledgment
//    in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
//    and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#ifndef TGUI_SCROLLBAR_HPP
#define TGUI_SCROLLBAR_HPP

#include <TGUI/Widget.hpp>
#include <TGUI/Renderers/ScrollbarRenderer.hpp>
#include <TGUI/CopiedSharedPtr.hpp>

#include <chrono>

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace tgui
{
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// @brief Scrollbar widget
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    class TGUI_API Scrollbar : public Widget
    {
    public:

        using Ptr = std::shared_ptr<Scrollbar>; //!< Shared widget pointer
        using ConstPtr = std::shared_ptr<const Scrollbar>; //!< Shared constant widget pointer

        static constexpr const char StaticWidgetType[] = "Scrollbar"; //!< Type name of the widget

        /// @brief Defines when the scrollbar shows up
        enum class Policy
        {
            Automatic,  //!< Show the scrollbar only when needed (default)
            Always,     //!< Always show the scrollbar, even when the contents fits
            Never       //!< Never show the scrollbar, even if the contents does not fit
        };

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /// @brief Constructor
        /// @param typeName     Type of the widget
        /// @param initRenderer Should the renderer be initialized? Should be true unless a derived class initializes it.
        /// @see create
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        Scrollbar(const char* typeName = StaticWidgetType, bool initRenderer = true);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Creates a new scrollbar widget
        /// @param orientation  Whether the scrollbar lies horizontally or vertically
        /// @return The new scrollbar
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD static Scrollbar::Ptr create(Orientation orientation = Orientation::Vertical);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Makes a copy of another scrollbar
        ///
        /// @param scrollbar  The other scrollbar
        ///
        /// @return The new scrollbar
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD static Scrollbar::Ptr copy(const Scrollbar::ConstPtr& scrollbar);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the renderer, which gives access to functions that determine how the widget is displayed
        /// @return Temporary pointer to the renderer that may be shared with other widgets using the same renderer
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD ScrollbarRenderer* getSharedRenderer() override;
        TGUI_NODISCARD const ScrollbarRenderer* getSharedRenderer() const override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the renderer, which gives access to functions that determine how the widget is displayed
        /// @return Temporary pointer to the renderer
        /// @warning After calling this function, the widget has its own copy of the renderer and it will no longer be shared.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD ScrollbarRenderer* getRenderer() override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes the size of the scrollbar
        ///
        /// @param size  The new size of the scrollbar
        ///
        /// Note that the Orientation propery is changed by this function based on the given width and height,
        /// unless the setOrientation function was previously called to explicitly select the orientation.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setSize(const Layout2d& size) override;
        using Widget::setSize;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Sets a maximum value
        /// @param maximum  The new maximum value
        ///
        /// When the value is bigger than (maximum - viewportSize), the value is set to maximum - viewportSize.
        /// The default maximum value is 10.
        /// @see setViewportSize
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setMaximum(unsigned int maximum);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the maximum value
        ///
        /// @return The current maximum value
        ///
        /// The default maximum value is 10.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD unsigned int getMaximum() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes the current value
        /// @param value  The new value
        ///
        /// The value has to be smaller than maximum - viewportSize.
        /// @see setViewportSize
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setValue(unsigned int value);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the current value
        ///
        /// @return The current value
        ///
        /// The default value is 0.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD unsigned int getValue() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes the viewport size
        ///
        /// @param viewport  The new viewport size
        ///
        /// If the contents through which the scrollbar can scroll is 600 pixels of which only 200 pixels are visible on the
        /// screen then the viewport size should be set to 200 and the maximum should be set to 600. The thumb will occupy
        /// one third of the scrollbar track in this case. The possible scrollbar values are in the range [0, 400] in this case.
        ///
        /// Until the maximum is bigger than this value, no scrollbar will be drawn.
        /// You can however choose to always draw the scrollbar by calling setPolicy(Policy::Always).
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setViewportSize(unsigned int viewport);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the viewport size
        /// @see setViewportSize
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD unsigned int getViewportSize() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the maximum value that can be set with the setValue function
        ///
        /// @return getMaximum() - getViewportSize() if getMaximum() >= getViewportSize(), otherwise 0
        ///
        /// @since TGUI 1.4
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD unsigned int getMaxValue() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes how much the value changes when scrolling or pressing one of the arrows of the scrollbar
        ///
        /// @param scrollAmount  How far should the scrollbar scroll when an arrow is clicked?
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setScrollAmount(unsigned int scrollAmount);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns how much the value changes when scrolling or pressing one of the arrows of the scrollbar
        ///
        /// @return How far should the scrollbar scroll when an arrow is clicked?
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD unsigned int getScrollAmount() const;

#ifndef TGUI_REMOVE_DEPRECATED_CODE
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes whether the scrollbar should hide automatically or not
        /// @param autoHide  Should the scrollbar be invisible when you can't scroll?
        ///
        /// When true (default), the scrollbar will not be drawn when the maximum is smaller than the viewportSize.
        /// @see setViewportSize
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_DEPRECATED("Use setPolicy instead") void setAutoHide(bool autoHide);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns whether the scrollbar is hiding automatically or not
        ///
        /// When true (default), the scrollbar will not be drawn when the maximum is smaller than the viewportSize.
        /// @see setViewportSize
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_DEPRECATED("Use getPolicy instead") TGUI_NODISCARD bool getAutoHide() const;
#endif

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes when the scrollbar should be displayed
        /// @param policy  The policy for displaying the scrollbar
        /// @since TGUI 1.5
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setPolicy(Policy policy);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns when the scrollbar should be displayed
        /// @return The policy for displaying the scrollbar
        /// @since TGUI 1.5
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD Scrollbar::Policy getPolicy() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns whether the scrollbar is currently visible
        /// @return Is the scrollbar visible?
        ///
        /// If setVisible(false) was called to make the widget invisible then this function always returns false,
        /// irrelevant of the policy. Otherwise the returned value depends on the policy:
        /// - If the policy is Never then this function always returns false.
        /// - If the policy is Always then the function returns true.
        /// - If the policy is Automatic then the function returns true if the maximum is higher than the viewport size.
        ///
        /// When this function returns false, the scrollbar is hidden and acts as if setVisible(false) was called.
        ///
        /// @since TGUI 1.5
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD bool isShown() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes whether the scrollbar lies horizontally or vertically
        /// @param vertical  Should the scrollbar lie vertically?
        ///
        /// This function will swap the width and height of the scrollbar if it didn't lie in the wanted direction.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_DEPRECATED("Use setOrientation instead") void setVerticalScroll(bool vertical);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns whether the scrollbar lies horizontally or vertically
        /// @return Does the scrollbar lie vertically?
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_DEPRECATED("Use getOrientation instead") TGUI_NODISCARD bool getVerticalScroll() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes whether the scrollbar lies horizontally or vertically
        /// @param orientation  Orientation of the scrollbar
        ///
        /// This function will swap the width and height of the scrollbar if it didn't lie in the wanted direction.
        ///
        /// @since TGUI 1.4
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setOrientation(Orientation orientation);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns whether the scrollbar lies horizontally or vertically
        /// @return Orientation of the scrollbar
        /// @since TGUI 1.4
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD Orientation getOrientation() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the default width of the scrollbar
        /// @return Default scrollbar width
        ///
        /// The default width is the value the scrollbar has on construction or the size of the texture once a texture is set.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD float getDefaultWidth() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns whether the widget can gain focus
        /// @return Can the widget be focused?
        ///
        /// For the scrollbar widget this function returns true when the scrollbar is shown and false when it is hidden.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD bool canGainFocus() const override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns whether the mouse position (which is relative to the parent widget) lies on top of the widget
        ///
        /// @return Is the mouse on top of the widget?
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD bool isMouseOnWidget(Vector2f pos) const override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        bool leftMousePressed(Vector2f pos) override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void leftMouseReleased(Vector2f pos) override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void mouseMoved(Vector2f pos) override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        bool scrolled(float delta, Vector2f pos, bool touch) override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void leftMouseButtonNoLongerDown() override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Draw the widget to a render target
        ///
        /// @param target Render target to draw to
        /// @param states Current render states
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void draw(BackendRenderTarget& target, RenderStates states) const override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    protected:

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Updates the scrollbar after a size change
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void updateSize();

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Retrieves a signal based on its name
        ///
        /// @param signalName  Name of the signal
        ///
        /// @return Signal that corresponds to the name
        ///
        /// @throw Exception when the name does not match any signal
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD Signal& getSignal(String signalName) override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Function called when one of the properties of the renderer is changed
        ///
        /// @param property  Name of the property that was changed
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void rendererChanged(const String& property) override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Saves the widget as a tree node in order to save it to a file
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD std::unique_ptr<DataIO::Node> save(SavingRenderersMap& renderers) const override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Loads the widget from a tree of nodes
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void load(const std::unique_ptr<DataIO::Node>& node, const LoadingRenderersMap& renderers) override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Updates the position of the thumb based on the current value of the slider
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void updateThumbPosition();

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Makes a copy of the widget
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD Widget::Ptr clone() const override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    private:

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Schedules a callback to regularly change the value of the scrollbar as long as the mouse remains pressed on an arrow
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void callMousePressPeriodically(std::chrono::time_point<std::chrono::steady_clock> clickedTime, bool repeatedCall);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    public:

        SignalUInt onValueChange = {"ValueChanged"}; //!< Value of the scrollbar changed. Optional parameter: new value

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    protected:

        enum class Part
        {
            None,
            Track,
            Thumb,
            ArrowUp,
            ArrowDown
        };

        // Keep track on which part of the scrollbar the mouse is standing
        Part m_mouseHoverOverPart = Part::None;

        // When the mouse went down, did it go down on top of the thumb? If so, where?
        bool m_mouseDownOnThumb = false;
        Vector2f m_mouseDownOnThumbPos;

        unsigned int m_maximum = 10;
        unsigned int m_value = 0;

        // Maximum should be above this value before the scrollbar is needed
        unsigned int m_viewportSize = 1;

        Orientation m_orientation = Orientation::Vertical; // Is the scrollbar drawn horizontally or vertically?
        Orientation m_imageOrientation = Orientation::Vertical;  // Does the loaded image lie horizontally or vertically?
        bool m_orientationLocked = false; // TGUI_NEXT: Remove property and make locked the default

        // How far should the value change when pressing one of the arrows?
        unsigned int m_scrollAmount = 1;

        // Defines when the scrollbar should be visible or hidden
        Scrollbar::Policy m_policy = Scrollbar::Policy::Automatic;

        // Did the mouse went down on one of the arrows?
        bool m_mouseDownOnIncreaseArrow = false;
        bool m_mouseDownOnDecreaseArrow = false;
        std::chrono::time_point<std::chrono::steady_clock> m_lastMousePressTime;

        bool m_sizeSet = false; // Has setSize been called?

        std::chrono::steady_clock::time_point m_lastSuccessfulScrollTime; // Timestamp of the last mouse wheel scroll event
        Vector2f m_lastSuccessfulScrollPos; // Mouse position at the last mouse wheel scroll event

        FloatRect m_track;
        FloatRect m_thumb;
        FloatRect m_arrowUp;
        FloatRect m_arrowDown;

        Sprite m_spriteTrack;
        Sprite m_spriteTrackHover;
        Sprite m_spriteThumb;
        Sprite m_spriteThumbHover;
        Sprite m_spriteArrowUp;
        Sprite m_spriteArrowUpHover;
        Sprite m_spriteArrowDown;
        Sprite m_spriteArrowDownHover;

        // Cached renderer properties
        Color m_thumbColorCached;
        Color m_thumbColorHoverCached;
        Color m_trackColorCached;
        Color m_trackColorHoverCached;
        Color m_arrowColorCached;
        Color m_arrowColorHoverCached;
        Color m_arrowBackgroundColorCached;
        Color m_arrowBackgroundColorHoverCached;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    };

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// @brief Wrapper around scrollbar to be used inside widgets that need a scrollbar
    ///
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    class TGUI_API ScrollbarChildWidget : public Scrollbar
    {
    public:

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Default constructor
        /// @param orientation  Whether the scrollbar lies horizontally or vertically
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ScrollbarChildWidget(Orientation orientation = Orientation::Vertical); // TGUI_NEXT: No more default option

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns whether the left mouse button has been pressed on top of the thumb of the scrollbar
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD bool isMouseDownOnThumb() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Draw the widget to a render target
        ///
        /// @param target Render target to draw to
        /// @param states Current render states
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void draw(BackendRenderTarget& target, RenderStates states) const override;
    };

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// @brief Class returned by widgets that have a scrollbar to let the user access scrollbar properties
    /// @since TGUI 1.5
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    class TGUI_API ScrollbarAccessor
    {
    public:

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Constructor
        /// @param scrollbar  The scrollbar to which this class provides access
        /// @param valueChangedCallback  Function to be called when setValue is called on this class
        /// @param policyChangedCallback  Function to be called when setPolicy is called on this class
        /// @param scrollAmountChangedCallback  Function to be called when setScrollAmount is called on this class
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ScrollbarAccessor(ScrollbarChildWidget& scrollbar,
                          std::function<void()> valueChangedCallback,
                          std::function<void()> policyChangedCallback,
                          std::function<void()> scrollAmountChangedCallback);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes the current value of the scrollbar
        /// @param value  The new value
        /// @see Scrollbar::setValue
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setValue(unsigned int value);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the current value of the scrollbar
        /// @return The current value
        /// @see Scrollbar::getValue
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD unsigned int getValue() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes how much the value changes when scrolling or pressing one of the arrows of the scrollbar
        /// @param scrollAmount  How far should the scrollbar scroll when an arrow is clicked?
        /// @warning Widgets may overwrite this value when e.g. the text size changes
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setScrollAmount(unsigned int scrollAmount);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns how much the value changes when scrolling or pressing one of the arrows of the scrollbar
        /// @return How far should the scrollbar scroll when an arrow is clicked?
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD unsigned int getScrollAmount() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes when the scrollbar should be displayed
        /// @param policy  The policy for displaying the scrollbar
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setPolicy(Scrollbar::Policy policy);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns when the scrollbar should be displayed
        /// @return The policy for displaying the scrollbar
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD Scrollbar::Policy getPolicy() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the current maximum of the scrollbar
        /// @return The current maximum
        /// @see Scrollbar::getMaximum
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD unsigned int getMaximum() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the current viewport size of the scrollbar
        /// @return The current viewport size
        /// @see Scrollbar::getViewportSize
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD unsigned int getViewportSize() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the current maximum value of the scrollbar
        /// @return The current maximum value
        /// @see Scrollbar::getMaxValue
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD unsigned int getMaxValue() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns whether the scrollbar is currently visible
        /// @return Is the scrollbar visible?
        /// @see Scrollbar::isShown
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD bool isShown() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the width of the scrollbar
        /// @return Scrollbar width. This is size.x for a vertical scrollbar, but size.y for a horizontal scrollbar.
        ///
        /// The width of the scrollbar can be changed via the renderer of the widget that contains the scrollbar:
        /// @code
        /// widget->getRenderer()->setScrollbarWidth(width);
        /// @endcode
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD float getWidth() const;

    private:
        ScrollbarChildWidget* m_scrollbar;
        std::function<void()> m_valueChangedCallback;
        std::function<void()> m_policyChangedCallback;
        std::function<void()> m_scrollAmountChangedCallback;
    };

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// @brief Base class for widgets with a single scrollbar
    ///
    /// This class exists so that these widgets wouldn't need to define their own copy constructor and assignment operators.
    /// @since TGUI 1.5
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    class TGUI_API ScrollbarChildInterface
    {
    public:

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Default constructor
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ScrollbarChildInterface();

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Copy constructor
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ScrollbarChildInterface(const ScrollbarChildInterface&);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Move constructor
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ScrollbarChildInterface(ScrollbarChildInterface&&) noexcept;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Destructor
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        virtual ~ScrollbarChildInterface() = default;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Overload of copy assignment operator
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ScrollbarChildInterface& operator=(const ScrollbarChildInterface&);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Move assignment
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ScrollbarChildInterface& operator=(ScrollbarChildInterface&&) noexcept;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns an object that provides access to the widget's scrollbar
        /// @return Pointer to object that allows reading scrollbar properties and allows setting some of them
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD ScrollbarAccessor* getScrollbar();

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns an object that provides access to the widget's scrollbar
        /// @return Pointer to object that allows reading scrollbar properties (there is also a non-const function for setting)
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD const ScrollbarAccessor* getScrollbar() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    protected:

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Called when the value of the scrollbar has been changed via getScrollbar()->setValue(...)
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        virtual void scrollbarValueChanged();

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Called when the policy of the scrollbar has been changed via getScrollbar()->setPolicy(...)
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        virtual void scrollbarPolicyChanged();

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Called when the scroll amount of the scrollbar has been changed via getScrollbar()->setScrollAmount(...)
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        virtual void scrollbarScrollAmountChanged();

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief This function should be called inside the save function in order to save the configured scrollbar policy
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void saveScrollbarPolicy(std::unique_ptr<DataIO::Node>& node) const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief This function should be called inside the load function in order to load the wanted scrollbar policy
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void loadScrollbarPolicy(const std::unique_ptr<DataIO::Node>& node);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    protected:

        CopiedSharedPtr<ScrollbarChildWidget> m_scrollbar;
        ScrollbarAccessor m_scrollbarAccessor;
    };

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// @brief Base class for widgets with both a vertical and horizontal scrollbar
    ///
    /// This class exists so that these widgets wouldn't need to define their own copy constructor and assignment operators.
    /// @since TGUI 1.5
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    class TGUI_API DualScrollbarChildInterface
    {
    public:

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Default constructor
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        DualScrollbarChildInterface();

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Copy constructor
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        DualScrollbarChildInterface(const DualScrollbarChildInterface&);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Move constructor
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        DualScrollbarChildInterface(DualScrollbarChildInterface&&) noexcept;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Destructor
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        virtual ~DualScrollbarChildInterface() = default;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Overload of copy assignment operator
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        DualScrollbarChildInterface& operator=(const DualScrollbarChildInterface&);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Move assignment
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        DualScrollbarChildInterface& operator=(DualScrollbarChildInterface&&) noexcept;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns an object that provides access to the widget's vertical scrollbar
        /// @return Pointer to object that allows reading scrollbar properties and allows setting some of them
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD ScrollbarAccessor* getVerticalScrollbar();

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns an object that provides access to the widget's vertical scrollbar
        /// @return Pointer to object that allows reading scrollbar properties (there is also a non-const function for setting)
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD const ScrollbarAccessor* getVerticalScrollbar() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns an object that provides access to the widget's horizontal scrollbar
        /// @return Pointer to object that allows reading scrollbar properties and allows setting some of them
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD ScrollbarAccessor* getHorizontalScrollbar();

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns an object that provides access to the widget's horizontal scrollbar
        /// @return Pointer to object that allows reading scrollbar properties (there is also a non-const function for setting)
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD const ScrollbarAccessor* getHorizontalScrollbar() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    protected:

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Called when the value of one of the scrollbars has been changed by calling either
        ///        getVerticalScrollbar()->setValue(...) or getHorizontalScrollbar()->setValue(...)
        ///
        /// @param orientation  Vertical or Horizontal depending on which scrollbar triggered the callback
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        virtual void scrollbarValueChanged(Orientation orientation);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Called when the policy of one of the scrollbars has been changed calling either
        ///        getVerticalScrollbar()->setPolicy(...) or getHorizontalScrollbar()->setPolicy(...)
        ///
        /// @param orientation  Vertical or Horizontal depending on which scrollbar triggered the callback
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        virtual void scrollbarPolicyChanged(Orientation orientation);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Called when the scroll amount of one of the scrollbars has been changed calling either
        ///        getVerticalScrollbar()->setScrollAmount(...) or getHorizontalScrollbar()->setScrollAmount(...)
        ///
        /// @param orientation  Vertical or Horizontal depending on which scrollbar triggered the callback
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        virtual void scrollbarScrollAmountChanged(Orientation orientation);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief This function should be called inside the save function in order to save the configured scrollbar policies
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void saveScrollbarPolicies(std::unique_ptr<DataIO::Node>& node) const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief This function should be called inside the load function in order to load the wanted scrollbar policies
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void loadScrollbarPolicies(const std::unique_ptr<DataIO::Node>& node);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    protected:

        CopiedSharedPtr<ScrollbarChildWidget> m_verticalScrollbar;
        CopiedSharedPtr<ScrollbarChildWidget> m_horizontalScrollbar;
        ScrollbarAccessor m_verticalScrollbarAccessor;
        ScrollbarAccessor m_horizontalScrollbarAccessor;
    };

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#endif // TGUI_SCROLLBAR_HPP
